package adm

import (
	"database/sql"
	"git.oschina.net/janpoem/go-agi"
	"unsafe"
	"time"
	"fmt"
	"strings"
)

const (
	T_STR = iota
	T_INT
	T_FLOAT
)


type ResultSet struct {
	Columns     []string
	count       int
	rows        []*ResultRow
	Error       error
}

type ResultRow struct {
	rs              *ResultSet
	values          [][]byte
	Error           error
}

type ExecResult struct {
	QueryType       QueryType
	LastInsertId    int64
	RowsAffected    int64
	Error           error
}


func NewQueryResult(rawRows *sql.Rows, err error) *ResultSet {
	rs := &ResultSet{Error: err }
	if err == nil && rawRows != nil {
		columns, error := rawRows.Columns()
		if error == nil {
			rs.Columns = columns
			length := len(columns)
			rawBytes := make([][]byte, length)
			dest := make([]interface{}, length)
			for i, _ := range rawBytes {
				dest[i] = &rawBytes[i] // Put pointers to each string in the interface slice
			}
			for rawRows.Next() {
				err := rawRows.Scan(dest...)
				rs.AddRow(rawBytes, err)
			}
			rs.count = len(rs.rows)
		}
		rawRows.Close()
	}
	return rs
}

func NewExecResult(t QueryType, rs sql.Result, err error) *ExecResult {
	result := &ExecResult{QueryType: t, Error: err }
	if err == nil {
		// 忽略id的错误
		id, _ := rs.LastInsertId() // 注意，如果插入行数超过1个，这个id只能代表第一个成功插入的id
		rows, err := rs.RowsAffected()
		result.Error = err
		result.LastInsertId = id
		result.RowsAffected = rows
	}
	return result
}

func (this *ResultSet) AddRow(rawBytes [][]byte, error error) {
	newSlice := make([][]byte, len(rawBytes))
	copy(newSlice, rawBytes)
	this.rows = append(this.rows, &ResultRow{rs: this, values: newSlice, Error: error })
}

func (this *ResultSet) GetErr() error {
	return this.Error
}

func (this *ResultSet) HasErr() bool {
	return this.Error != nil
}

func (this *ResultSet) Count() int {
	return this.count
}

func (this *ResultSet) IsEmpty() bool {
	return this.Error != nil || this.count <= 0
}

func (this *ResultSet) Each(fn func(int, *ResultRow)) *ResultSet {
	count := this.Count()
	if count > 0 {
		for i := 0; i < count; i++ {
			fn(i, this.rows[i])
		}
	}
	return this
}

func (this *ResultSet) Rows() []*ResultRow {
	return this.rows
}

func (this *ResultSet) Row(index int) *ResultRow {
	if this.IsEmpty() {
		return nil
	}
	index = agi.OffsetIndex(index, this.count)
	return this.rows[index]
}

func (this *ResultSet) ColumnOf(field string) int {
	if this.IsEmpty() {
		return -1
	}
	for i, l := 0, len(this.Columns); i < l; i++ {
		if field == this.Columns[i] {
			return i
		}
	}
	return -1
}

func (this *ResultRow) GetRow() *ResultRow {
	return this
}

func (this *ResultRow) IsExists() bool {
	if this == nil {
		return false
	}
	if this.Error != nil {
		return false
	}
	if this.rs != nil {
		return len(this.values) > 0
	}
	return false
}

func (this *ResultRow) IsNew() bool {
	return !this.IsExists()
}

func (this *ResultRow) getRaw(index int) []byte {
	if index > -1 && this.values[index] != nil {
		return this.values[index]
	}
	return []byte{}
}

// 默认返回byte
func (this *ResultRow) Get(f string) []byte {
	return this.getRaw(this.rs.ColumnOf(f))
}

func (this *ResultRow) Str(f string) string {
	v := this.Get(f)
	// @see http://studygolang.com/articles/2909
	return *(*string)(unsafe.Pointer(&v))
}

func (this *ResultRow) Int(field string) int {
	return agi.StrToFInt(this.Str(field)) // 这里用转成浮点整型，就是最大可能的取到值
}

func (this *ResultRow) Int64(field string) int64 {
	return agi.StrToFInt64(this.Str(field)) // 这里用转成浮点整型，就是最大可能的取到值
}

func (this *ResultRow) Float(field string) float64 {
	return agi.StrToFloat(this.Str(field))
}

func (this *ResultRow) Round(field string, places int) float64 {
	return agi.Round(this.Float(field), places)
}

// 数据库一般都会习惯用0, 1来表达值false or true
// 也有的时候，通过 0 => 强制等价于false
func (this *ResultRow) Bool(field string) bool {
	return agi.StrToIntBool(this.Str(field))
}

// 用整型表达的时间值，因为我们已经习惯了用整型值来表达时间
// 不过为了预留配对标准的时间戳、Data、Time，暂时不占用Time的方法名
// 这个方法还是应该只返回一个结果
func (this *ResultRow) IntTime(field string) time.Time {
	return agi.IntToTime(this.Int64(field))
}

func (this *ResultRow) Compare(values map[string]interface{}) map[string]interface{} {
	diff := make(map[string]interface{})
	for key, value := range values {
		raw := this.Str(key)
		if !strings.EqualFold(fmt.Sprint(value), raw) {
			fmt.Println(fmt.Sprint(value), raw)
			diff[key] = value
		}
	}
	return diff
}

func (this *ResultRow) Fetch(obj Model) Model {
	Bind(this, obj)
	return obj
}

func (this *ExecResult) ExecError() error {
	if this == nil {
		return nil
	}
	if this.Error == nil {
		return nil
	}
	return this.Error
}

func (this *ExecResult) ExecHasError() bool {
	return this.ExecError() != nil
}

func (this *ExecResult) setQueryType(t QueryType) *ExecResult {
	this.QueryType = t
	return this
}

func (this *ExecResult) IsSuccess() bool {
	if this == nil {
		return false
	}
	if this.RowsAffected > 0 {
		return true
	}
	return false
}

func (this *ExecResult) IsDelete() bool {
	if this == nil {
		return false
	}
	if this.IsSuccess() && this.QueryType == QUERY_DELETE {
		return true
	}
	return false
}

func (this *ExecResult) IsCreate() bool {
	if this == nil {
		return false
	}
	if this.IsSuccess() && this.QueryType == QUERY_INSERT {
		return true
	}
	return false
}

func (this *ExecResult) IsUpdate() bool {
	if this == nil {
		return false
	}
	if this.IsSuccess() && this.QueryType == QUERY_UPDATE {
		return true
	}
	return false
}

func (this *ExecResult) IsSave() bool {
	if this == nil {
		return false
	}
	if this.IsCreate() || this.IsUpdate() {
		return true
	}
	return false
}
